package org.examples.java.lang;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;

import junit.framework.Assert;

import org.junit.Test;

public class ClassLoaderTest {

    @Test
    public void testGetSystemClassLoader() {
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader systemClassLoader2 = ClassLoader.getSystemClassLoader();

        assertNotNull(systemClassLoader);
        assertSame(systemClassLoader, systemClassLoader2);
        // test user classes
        assertSame(systemClassLoader, ClassLoaderTest.class.getClassLoader());
        // test third-party classes
        assertSame(systemClassLoader, Test.class.getClassLoader());
    }

    @Test
    public void testGetParent() {
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader extensionClassLoader = systemClassLoader.getParent();

        assertNotNull(extensionClassLoader);
        assertNotSame(systemClassLoader, extensionClassLoader);

        // bootstrap class loader means that the parent class loader property is null
        ClassLoader bootstrapClassLoader = extensionClassLoader.getParent();
        assertNull(bootstrapClassLoader);
    }

    @Test
    public void testStringClassGetClassLoaderIsBootstrapClassLoader() {
        assertNull(String.class.getClassLoader());
    }

    @Test
    public void testGetSystemResource() {
        Assert.assertNull(ClassLoader.getSystemResource("ClassLoaderTest"));
        Assert.assertNull(ClassLoader.getSystemResource("ClassLoaderTest.class"));
        Assert.assertNull(ClassLoader.getSystemResource("ClassLoaderTest.java"));
        Assert.assertNull(ClassLoader.getSystemResource("org/examples/java/lang/ClassLoaderTest.java"));
        Assert.assertNotNull(ClassLoader.getSystemResource("org/examples/java/lang/ClassLoaderTest.class"));
        // resource in the class path folder
        Assert.assertNotNull(ClassLoader.getSystemResource("empty.file"));
    }

    @Test
    public void testGetSystemResourcesEmptyName() throws IOException {
        Enumeration<URL> resourcesWithEmptyName = ClassLoader.getSystemResources("");
        boolean hasClassPathFolders = false;
        for(; resourcesWithEmptyName.hasMoreElements() && !hasClassPathFolders;) {
            URL url = resourcesWithEmptyName.nextElement();
            // Ant and eclipse have different class path folders
            hasClassPathFolders = url.getPath().endsWith("/bin/") || url.getPath().endsWith("/build/classes/");
        }
        assertTrue(hasClassPathFolders);
    }

    @Test
    public void testGetSystemResources() throws IOException {
        Enumeration<URL> resources = ClassLoader.getSystemResources("empty.file");
        assertTrue(resources.hasMoreElements());
        URL url = resources.nextElement();
        assertTrue(url.getPath().endsWith("/bin/empty.file") || url.getPath().endsWith("/resources/empty.file"));
    }

    @Test
    public void testGetSystemResourceAsStream() throws IOException {
        InputStream stream = ClassLoader.getSystemResourceAsStream("empty.file");
        assertEquals( -1, stream.read());
        stream.close();
    }

    @Test
    public void testGetResource() {
        ClassLoader classLoader = this.getClass().getClassLoader();
        Assert.assertNull(classLoader.getResource("ClassLoaderTest"));
        Assert.assertNull(classLoader.getResource("ClassLoaderTest.class"));
        Assert.assertNull(classLoader.getResource("ClassLoaderTest.java"));
        Assert.assertNull(classLoader.getResource("org/examples/java/lang/ClassLoaderTest.java"));
        Assert.assertNotNull(classLoader.getResource("org/examples/java/lang/ClassLoaderTest.class"));
        // resource in the class path folder
        Assert.assertNotNull(classLoader.getResource("empty.file"));
    }

    @Test
    public void testGetResources() throws IOException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        Enumeration<URL> resources = classLoader.getResources("empty.file");
        assertTrue(resources.hasMoreElements());
        URL url = resources.nextElement();
        assertTrue(url.getPath().endsWith("/bin/empty.file") || url.getPath().endsWith("/resources/empty.file"));
    }

    @Test
    public void testGetResourcesEmptyName() throws IOException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        ClassLoader extClassLoader = ClassLoader.getSystemClassLoader().getParent();
        Enumeration<URL> resources = classLoader.getResources("");
        Enumeration<URL> resources2 = extClassLoader.getResources("");

        assertFalse(Collections.list(resources).equals(Collections.list(resources2)));
    }

    @Test
    public void testGetResourceAsStream() throws IOException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        InputStream stream = classLoader.getResourceAsStream("empty.file");
        assertEquals( -1, stream.read());
        stream.close();
    }

    @Test
    public void testLoadClass() throws IOException, ClassNotFoundException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        Class<?> stringClass = classLoader.loadClass("java.lang.String");
        assertSame(String.class, stringClass);
    }

    @Test(expected = ClassNotFoundException.class)
    public void testLoadClassClassNotFound() throws IOException, ClassNotFoundException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        classLoader.loadClass("TestTest");
    }

    @Test
    public void testLoadClassUserClass() throws IOException, ClassNotFoundException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        Class<?> class1 = classLoader.loadClass("org.examples.java.lang.ClassLoaderTest");
        assertSame(this.getClass(), class1);
    }

    @Test(expected = ClassNotFoundException.class)
    public void testLoadClassUserClass2() throws IOException, ClassNotFoundException {
        ClassLoader extClassLoader = ClassLoader.getSystemClassLoader().getParent();
        extClassLoader.loadClass("org.examples.java.lang.ClassLoaderTest");
    }

    @Test
    public void testAssertionStatusSetters() throws IOException, ClassNotFoundException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        classLoader.setClassAssertionStatus("org.examples.java.lang.ClassLoaderTest", false);
        classLoader.setDefaultAssertionStatus(false);
        classLoader.setPackageAssertionStatus("bbb", true);
        classLoader.clearAssertionStatus();
        // for some reason assertion status changing has no effect
        // assert false : "Bad";
    }
}
